from src.detection.const import ENotifyTypeUI
from src.mode.const import EResultKey
from src.mode.detection.server import SmartEnergyUnit
from src.mode.enc_machine.const import EKeyState, EEncMachineRetKey
from src.mode.enc_machine.function import EncryptionMachine
from src.mode.protocol_698.app_connect_req import AppConnReq, ConnectMechInfo
from src.mode.protocol_698.protocol import Protocol
from src.mode.enc_machine.NegotiateResult import NegotiateResult, SessionData
from src.mode.protocol_698.session_key_dist import SessionKeyDist


class Support(object):
    def __init__(self, ip: str, port: str, path: str = "./dll/WinSocketServer.dll"):
        self.__protocol = Protocol()
        self.__enc_machine = EncryptionMachine(ip=ip, port=port, path_dell=path)
        self.__session_key_dist = SessionKeyDist(16)
        self.test_mode = 0  # 默认为0，test_mode=0时，此时为测试模式

    def frame_organize_698(self, cmd, datasets):
        """
        698 协议报文组织 TODO 待完善
        :return:
        """
        pass

    def frame_analysis_698(self, data: str):
        """
        698 协议报文解析 TODO 待完善
        :return:
        """
        pass

    """
    光伏协议转换器 与主站密钥协商
    """

    def enc_negotiate(self, device_address: str, energy_unit: SmartEnergyUnit, exec_notify) -> NegotiateResult:
        negotiate = NegotiateResult()
        session_data = SessionData()
        """
        第一步：读取通信地址（40010200）
        """
        if not device_address:
            result = self.__normal_trance_698(energy_unit, "0501014001020000",
                                              "AAAAAAAAAAAA", exec_notify)
            if not result:
                negotiate.ret = 1
                negotiate.msg = "获取通信地址请求失败"
                return negotiate
            parser_result = self.__parse_for_octet_string(result, "通信地址", exec_notify)
            if not parser_result[0]:
                negotiate.ret = 1
                negotiate.msg = "获取通信地址请求失败"
                return negotiate
            else:
                energy_unit_address = "".join(
                    tuple(parser_result[1][i] + parser_result[1][i + 1] for i in range(0, len(parser_result[1]), 2))[
                    ::-1])
                session_data.set_device_address(energy_unit_address)
        else:
            session_data.set_device_address(device_address)
        energy_unit_address = session_data.get_device_address()
        """
        第二步：读取表号（40020200）
        """
        result = self.__normal_trance_698(energy_unit, "0501014002020000",
                                          energy_unit_address, exec_notify)
        if not result:
            negotiate.ret = 1
            negotiate.msg = "获取表号请求失败"
            return negotiate
        parser_result = self.__parse_for_octet_string(result, "表号", exec_notify)
        if not parser_result[0]:
            negotiate.ret = 1
            negotiate.msg = "解析表号失败"
            return negotiate
        else:
            device_no = parser_result[1]
            session_data.set_device_no(device_no)
        """
        第三步：读取ESAM序列号（F1000200）
        """
        result = self.__normal_trance_698(energy_unit, "050101F100020000",
                                          energy_unit_address, exec_notify)
        if not result:
            negotiate.ret = 1
            negotiate.msg = "获取ESAM序列号请求失败"
            return negotiate
        parser_result = self.__parse_for_octet_string(result, "ESAM序列号", exec_notify)

        if not parser_result[0]:
            negotiate.ret = 1
            negotiate.msg = "解析ESAM序列号失败"
            return negotiate
        else:
            c_y_esam_no = parser_result[1]
            session_data.set_y_esam_no(c_y_esam_no)

        """
        第四步：读取对称密钥版本（F1000400）
        """
        result = self.__normal_trance_698(energy_unit, "050101F100040000",
                                          energy_unit_address, exec_notify)
        if not result:
            negotiate.ret = 1
            negotiate.msg = "获取对称密钥版本请求失败"
            return negotiate
        parser_result = self.__parse_for_octet_string(result, "对称密钥版本", exec_notify)

        if not parser_result[0]:
            negotiate.ret = 1
            negotiate.msg = "解析对称密钥版本失败"
            return negotiate
        else:
            c_flg = parser_result[1]
        """
        第五步：读取计数器（F1000700）
        """
        result = self.__normal_trance_698(energy_unit, "050101F100070000",
                                          energy_unit_address, exec_notify)
        if not result:
            negotiate.ret = 1
            negotiate.msg = "获取计数器请求失败"
            return negotiate
        parser_result = self.__parser_counter(result, exec_notify)

        if not parser_result[0]:
            negotiate.ret = 1
            negotiate.msg = "解析计数器失败"
            return negotiate
        else:
            c_asctr = parser_result[1]

        """
        第六步：读取安全模式参数（F1010200）
        """
        result = self.__normal_trance_698(energy_unit, "050101F101020000",
                                          energy_unit_address, exec_notify)
        if not result:
            negotiate.ret = 1
            negotiate.msg = "获取安全模式参数请求失败"
            return negotiate
        parser_result = self.__parser_read_safe_mode_parameter(result, exec_notify)

        if not parser_result[0]:
            negotiate.ret = 1
            negotiate.msg = "解析安全模式参数失败"
            return negotiate
        else:
            safe_mode_parameter = parser_result[1]

        """
        第七步：与加密机主站会话协商
        """
        if self.test_mode != 0:
            result = self.__enc_machine.obj_6f_formal_init_session(EKeyState.PRIVATE_KEY, c_y_esam_no, c_asctr, "01")
            if not result:
                negotiate.ret = 1
                negotiate.msg = "与加密机主站会话协商失败"
                return negotiate
            elif result.code != 0:
                negotiate.ret = 1
                negotiate.msg = "与加密机主站会话协商失败"
                return negotiate
            else:
                data = result.data
                c_out_rand_host = data.get(EEncMachineRetKey.MASTER_RANDOM.value)
                m1 = data.get(EEncMachineRetKey.CIPHERTEXT_M1)
                s1 = data.get(EEncMachineRetKey.SIGN_S1)
            """
        第八步：应用连接
            """
            acr = AppConnReq()
            acr.version = "0010"
            acr.protocol = "FFFFFFFFFFFFFFFF"
            acr.function = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
            acr.send_max_size = "0400"
            acr.receive_max_size = "0400"
            acr.receive_max_win_size = "01"
            acr.max_apdu_size = "0400"
            acr.link_over_time = "00000064"
            mech_info = ConnectMechInfo()
            mech_info.m1 = m1
            mech_info.s1 = s1
            acr.conn_mech_info = mech_info
            apdu = acr.format_connect_req_apdu()
            result = self.__normal_trance_698(energy_unit, apdu,
                                              energy_unit_address, exec_notify)
            if not result:
                negotiate.ret = 1
                negotiate.msg = "应用连接请求失败"
                return negotiate
            parser_result = self.__parser_app_connect_req(result, exec_notify)

            if not parser_result[0]:
                negotiate.ret = 1
                negotiate.msg = "应用连接请求解析失败"
                return negotiate
            else:
                security_data = parser_result[1]
                rn = security_data.get("rn")
                sign = security_data.get("sign")
            """
        第九步：加密机协商验证
            """
            result = self.__enc_machine.obj_6f_formal_verify_session(EKeyState.PRIVATE_KEY, c_y_esam_no,
                                                                     c_out_rand_host,
                                                                     rn,
                                                                     sign)
            if result.code != -1:
                negotiate.ret = 1
                negotiate.msg = "与加密机协商验证失败"
                return negotiate
            else:
                data = result.data
                c_out_session_key = data.get(EEncMachineRetKey.SESSION_KEY.value)
                session_data.set_session_key(c_out_session_key)
                negotiate.set_data(session_data)
                negotiate.ret = 0
                negotiate.msg = "密钥协商成功"
                self.__session_key_dist[energy_unit_address] = c_out_session_key
        else:
            negotiate.set_data(session_data)
            print("此时为测试模式，此处应为与加密机协商验证")
            return negotiate

    def enc_data(self, unencrypted_data: str, y_esam_no: str, device_address: str) -> (bool, str, str):
        """
        数据加密
        """
        session_key = self.__session_key_dist[device_address]
        if not session_key:
            return 1, "", "会话密钥为空，请重新进行密钥协商"
        result = self.__enc_machine.obj_6f_formal_get_session_data(3, y_esam_no, session_key, 3, unencrypted_data)
        if not result:
            return 1, "", "安全传输加密失败"
        else:
            data = result.get_data()

            c_out_sid = data[EEncMachineRetKey.SID.value]  # 安全标识 SID
            c_out_attach_data = data[EEncMachineRetKey.ATTACH_DATA.value]  # 附加数据
            c_out_task_data = data[EEncMachineRetKey.CIPHERTEXT.value]  # 任务数据
            c_out_task_mac = data[EEncMachineRetKey.MAC]  # 任务数据MAC

            p698_code = (
                    "10"  # 10:安全请求
                    + "01"  # 00： 明文  01：密文
                    + format(len(c_out_task_data), '02X')  # 任务数据长度
                    + c_out_task_data  # 任务数据
                    + "00"  # 安全类型    0:数据验证码,SID_MAC,随机数    1:RN,随机数,数据MAC  2:RN_MAC 3:安全标识 SID
                    + c_out_sid  # 安全标识 SID
                    + format(len(c_out_attach_data), '02X')  # 附加数据长度
                    + c_out_attach_data  # 附加数据
                    + format(len(c_out_task_mac), '02X')  # 任务数据MAC长度
                    + c_out_task_mac  # 任务数据MAC
            )
            return 0, p698_code, "安全传输加密成功"

    def dec_data(self, encrypted_data: str, mac: str, device_address: str, y_esam_no: str) -> (bool, str, str):
        """
        数据解密
        """
        session_key = self.__session_key_dist[device_address]
        if not session_key:
            return 1, "", "会话密钥为空，请重新进行密钥协商"
        result = self.__enc_machine.obj_6f_formal_verify_meter_data(3, y_esam_no, session_key, encrypted_data, mac)
        if not result:
            return 1, "", "安全传输解密失败"
        else:
            data = result.get_data()
            out_data = data[EEncMachineRetKey.OUT_DATA]
            return 0, out_data, "安全传输解密成功"

    def __normal_trance_698(self, energy_unit: SmartEnergyUnit, link_user_data: str, address: str,
                            exec_notify) -> (bool, str, str):
        if len(address) == 6 * 2:
            logi_address = "00"
            device_address = address
        elif len(address) == 7 * 2:
            logi_address = address[0:2]
            device_address = address[2:]
        else:
            return 1, "", "入参address不合法"
        if 'A' in device_address.upper():
            address_type = "01"
        else:
            address_type = "00"

        frame698 = Protocol().set_address(address).set_link_user_data(link_user_data).set_logi_add(
            logi_address).set_address_type(address_type)
        frame_data = frame698.format_frame_string()
        ret = energy_unit.comm(frame_data)
        # frame_recv = ret["data"]  # 默认正确返回
        frame_recv = ret.data[EResultKey.RECEIVE_FRAME.value]  # 默认正确返回
        exec_notify(ENotifyTypeUI.NOTIFY_LOG, "执行结果：{}".format(ret))
        exec_notify(ENotifyTypeUI.NOTIFY_LOG, "接收的数据：{}".format(frame_recv))
        return str(frame_recv)

    def __parse_for_octet_string(self, frame_response: str, name: str, exec_notify) -> (bool, str, str):

        if frame_response[2] != "f":
            result = frame_response.replace("['", "")
            frame_data = result.replace("']", "")
        else:
            test = frame_response[10:]
            frame_data = test.replace("']", "")  # 使用 replace 方法替换成空字符串
        response_frame = Protocol().parser_frame(frame_data)
        link_user_data = response_frame.get_link_user_data()
        apdu = [link_user_data[i:i + 2] for i in range(0, len(link_user_data), 2)]
        dar = apdu[7]
        # 判断响应的dar是 01 还是 00
        if dar == "01":
            # 如果第八位是 01，则切出第九位和第十位
            data_type = apdu[8]
            content_length = int(apdu[9])
            # 根据octet_string的长度从字节数组中切片出数据
            content = ''.join(apdu[10:10 + content_length])
            # 输出通讯地址
            return 1, content, "解析成功"
        else:
            return 0, "", "解析失败"

    def __parser_counter(self, frame_response: str, exec_notify) -> (bool, str, str):
        if frame_response[2] != "f":
            result = frame_response.replace("['", "")
            frame_data = result.replace("']", "")
        else:
            test = frame_response[10:]
            frame_data = test.replace("']", "")  # 使用 replace 方法替换成空字符串
        response_frame = Protocol().parser_frame(frame_data)
        link_user_data = response_frame.get_link_user_data()
        apdu = [link_user_data[i:i + 2] for i in range(0, len(link_user_data), 2)]
        dar = apdu[7]
        # 判断响应的dar是 01 还是 00
        if dar == "01":
            # 如果dar是 01，则切出数据类型
            data_type = apdu[8]
            length = int(apdu[9])
            # 根据数据长度的长度从字节数组中切片出计数器
            counter = ""
            for i in range(length):
                start_index = 10 + i * 5
                counter = apdu[start_index:start_index + 5]
            return 1, counter, "解析成功"
        else:
            return 0, "", "解析失败"

    def __parser_read_safe_mode_parameter(self, frame_response: str, exec_notify) -> (bool, str, str):
        if frame_response[2] != "f":
            result = frame_response.replace("['", "")
            frame_data = result.replace("']", "")
        else:
            test = frame_response[10:]
            frame_data = test.replace("']", "")  # 使用 replace 方法替换成空字符串
        response_frame = Protocol().parser_frame(frame_data)
        link_user_data = response_frame.get_link_user_data()
        apdu = [link_user_data[i:i + 2] for i in range(0, len(link_user_data), 2)]
        dar = apdu[7]
        # 判断响应的dar是 01 还是 00
        if dar == "01":
            # 如果dar是 01，则切出数据类型
            data_type = apdu[8]
            length = int(apdu[9])
            # 根据数据长度的长度从字节数组中切片出安全模式参数
            safe_mode_parameter = apdu[10:10 + length]
            # 输出安全模式参数
            return 1, safe_mode_parameter, "解析成功"
        else:
            return 0, "", "解析失败"

    def __parser_app_connect_req(self, frame_response: str, exec_notify) -> (bool, dict, str):
        if frame_response[2] != "f":
            result = frame_response.replace("['", "")
            frame_data = result.replace("']", "")
        else:
            test = frame_response[10:]
            frame_data = test.replace("']", "")  # 使用 replace 方法替换成空字符串
        response_frame = Protocol().parser_frame(frame_data)
        link_user_data = response_frame.get_link_user_data()
        apdu = [link_user_data[i:i + 2] for i in range(0, len(link_user_data), 2)]
        factory_info = apdu[2:34]  # 服务器厂商版本信息
        version_info = apdu[34:36]  # 商定的应用层协议版本号
        protocol_info = apdu[36:44]  # 商定的协议一致性块
        function_info = apdu[44:60]  # 商定的功能一致性块
        send_max_size = apdu[60:62]  # 服务器发送帧最大尺寸
        receive_max_size = apdu[62:64]  # 服务器接收帧最大尺寸
        audio_max_size = apdu[64:65]  # 服务器接收帧最大窗口尺寸
        receive_max_min_size = apdu[65:67]  # 服务器最大可处理APDU尺寸
        link_over_time = apdu[67:71]  # 商定的应用连接超时时间
        connect_result = apdu[71]
        if connect_result == "00":
            connect_optional = apdu[72]
            if connect_optional == "01":
                rn_len = int(apdu[73], 16)
                rn = apdu[74:74 + rn_len]
                sign_len = int(apdu[74 + rn_len], 16)
                sign = apdu[74 + rn_len + 1:74 + rn_len + 1 + sign_len]
                security_data = {'rn': rn, 'sign': sign}
                return 1, security_data, "解析成功"
            else:
                return 0, None, "解析失败"
        else:
            return 0, None, "解析失败"

    """
    698安全传输
    """

    def safe_trance_698(self, link_user_data: str, y_esam_no: str, energy_unit: SmartEnergyUnit, address: str,
                        exec_notify) -> (bool, str, str):
        """
        获取加密数据
        """
        extension_address = ""
        if len(address) == 6 * 2:
            logi_address = "00"
            device_address = address
        elif len(address) == 7 * 2:
            logi_address = "02"
            extension_address = address[0:2]
            device_address = address[2:]
        else:
            return 1, "", "入参address不合法"
        if 'A' in device_address.upper():
            address_type = "01"
        else:
            address_type = "00"
        if self.test_mode == 0:
            print("device_address: ", device_address, "----", "address_type: ", address_type, "----", "logi_address: ",
                  logi_address, "----", "link_user_data: ", link_user_data)
            frame698 = Protocol().set_address(device_address).set_address_type(address_type).set_logi_add(
                logi_address).set_link_user_data(link_user_data).set_extension_address(extension_address)
        else:
            result = self.enc_data(link_user_data, y_esam_no, address)
            if not result or not result.is_success():
                return 1, "", "设置安全模式参数-安全传输加密失败"
            encrypted_data = result[1]
            frame698 = Protocol().set_address(device_address).set_address_type(address_type).set_logi_add(
                logi_address).set_link_user_data(encrypted_data)
        frame_data = frame698.format_frame_string()
        ret = energy_unit.comm(frame_data)
        # frame_recv = ret["data"]  # 默认正确返回
        frame_recv = ret.data[EResultKey.RECEIVE_FRAME.value]  # 默认正确返回
        exec_notify(ENotifyTypeUI.NOTIFY_LOG, "安全传输执行结果：{}".format(ret))
        exec_notify(ENotifyTypeUI.NOTIFY_LOG, "安全传输接收的数据：{}".format(frame_recv))
        if frame_recv is None or frame_recv == "":
            return ""
        """
        解析安全传输返回
        """
        frame698 = Protocol()
        re = str(frame_recv).replace("['", "")

        frame_data = re.replace("']", "")
        frame698.parser_frame(str(frame_data))
        response_link_data = frame698.get_link_user_data()
        if self.test_mode != 0:
            encrypted_data_len = int(response_link_data[4:6], 16)
            encrypted_data = response_link_data[6:6 + encrypted_data_len]
            mac = response_link_data[6 + encrypted_data_len + 6]

            """
        加密机解密加密数据
            """
            result = self.dec_data(encrypted_data, mac, address, y_esam_no)
            if not result or not result.is_success():
                return 1, "", "设置安全模式参数-安全传输解密失败"
            unencrypted_data = result[1]
            return 0, unencrypted_data, "安全传输成功"
        else:
            return response_link_data

    # 用于测试用例3
    def safe_not_trance_698(self, link_user_data: str, y_esam_no: str, energy_unit: SmartEnergyUnit, address: str,
                            exec_notify, type, start) -> (bool, str, str):
        # 通过此方法，可直接解析出APDU。例：850101200002000101031209E61200001200000101013320020101513061020000
        """
        获取加密数据
        """
        extension_address = ""
        if len(address) == 6 * 2:
            logi_address = "00"
            device_address = address
        elif len(address) == 7 * 2:
            logi_address = "02"
            extension_address = address[0:2]
            device_address = address[2:]
        else:
            return 1, "", "入参address不合法"
        if 'A' in device_address.upper():
            address_type = "01"
        else:
            address_type = "00"
        if self.test_mode == 0:
            frame698 = Protocol().set_address(device_address).set_address_type(address_type).set_logi_add(
                logi_address).set_link_user_data(link_user_data)
        else:
            result = self.enc_data(link_user_data, y_esam_no, address)
            if not result or not result.is_success():
                return 1, "", "设置安全模式参数-安全传输加密失败"
            encrypted_data = result[1]
            frame698 = Protocol().set_address(device_address).set_address_type(address_type).set_logi_add(
                logi_address).set_link_user_data(encrypted_data)
        frame_data = frame698.generate_frame_not68H(type, start)

        ret = energy_unit.comm(frame_data)

        # frame_recv = ret["data"]  # 默认正确返回
        frame_recv = ret.data[EResultKey.RECEIVE_FRAME.value]  # 默认正确返回
        exec_notify(ENotifyTypeUI.NOTIFY_LOG, "安全传输执行结果：{}".format(ret))
        exec_notify(ENotifyTypeUI.NOTIFY_LOG, "安全传输接收的数据：{}".format(frame_recv))
        if frame_recv is None:
            return ""
        """
        解析安全传输返回
        """
        frame698 = Protocol()
        re = str(frame_recv).replace("['", "")

        frame_data = re.replace("']", "")
        if not frame_data:
            return ""
        frame698.parser_frame(str(frame_data))
        response_link_data = frame698.get_link_user_data()
        if self.test_mode != 0:
            encrypted_data_len = int(response_link_data[4:6], 16)
            encrypted_data = response_link_data[6:6 + encrypted_data_len]
            mac = response_link_data[6 + encrypted_data_len + 6]

            """
        加密机解密加密数据
            """
            result = self.dec_data(encrypted_data, mac, address, y_esam_no)
            if not result or not result.is_success():
                return 1, "", "设置安全模式参数-安全传输解密失败"
            unencrypted_data = result[1]
            return 0, unencrypted_data, "安全传输成功"
        else:
            return response_link_data
